Repository navigation
Conversation
|
A few comments:
|
|
@pelesh I changed it so it uses the PowerFlow parser with very few changes. I do believe its valuable to have that as the input format only because it makes validation easier and there is practically unlimited data in I believed I addressed your other issues too. I am able to use the example app to change the objective/model, solve for new operating point, and start simulation in steady state at that new point! |
95913b8 to
5c4fb92
Compare
5c4fb92 to
49ad14b
Compare
pelesh
left a comment
There was a problem hiding this comment.
This is very nice work. I suggest limiting the scope of this PR to providing ACOPF module only. Imho, the integration with dynamic simulations modules should be done after ACOPF is tested and input file format specified.
979e671 to
a6e3ae1
Compare
superwhiskers
left a comment
There was a problem hiding this comment.
tests pass on my end. i think it works quite well, but there are a few implementation questions we should sort out before this moves forward.
additionally, if we are to continue with matpower, are we intending to handle capability curves for generators or the convention of a dispatchable load as a generator with negative real-power injection? if they are outside the intended scope, could we reject them and document that limitation?
i think these conventions, particularly the latter, strengthen the argument for going with our own format, as using an existing format such as matpower requires us to adhere to its conventions, which may not be desirable.
| /// Relative tolerance of the optimal cost against a reference cost | ||
| constexpr double COST_TOLERANCE = 1.0e-8; |
There was a problem hiding this comment.
is there not a situation in which one may want to configure this?
There was a problem hiding this comment.
Yes, the user will want to specify the tolerance. And this tolerance is too tight.
| /** | ||
| * @brief Coordinate entries in global indices | ||
| * | ||
| * Filled by `mapped_store` and `lower_store`. Assembly sums duplicate | ||
| * coordinates. | ||
| */ | ||
| struct CooEntries | ||
| { | ||
| std::vector<size_t> rows; | ||
| std::vector<size_t> cols; | ||
| std::vector<double> values; | ||
|
|
||
| void clear() | ||
| { | ||
| rows.clear(); | ||
| cols.clear(); | ||
| values.clear(); | ||
| } |
There was a problem hiding this comment.
you should parameterize this by index type / scalar type so that you don't need the static assertion that IdxT/ScalarT are size_t/double in the evaluators
| } | ||
|
|
||
| private: | ||
| __attribute__((always_inline)) inline static ScalarT lagrangian(const ModelT* model, |
There was a problem hiding this comment.
prefer [[gnu::always_inline]]
| * @tparam ModelT - model type with `VARIABLE_SIZE` local variables and | ||
| * `CONSTRAINT_SIZE` local constraints |
There was a problem hiding this comment.
VARIABLE_SIZE may be more accurately named VARIABLE_COUNT, NUM_VARIABLES, VARIABLE_ARRAY_SIZE, or something else. VARIABLE_SIZE to me read as "bit/byte width of individual variables"
| * @tparam ModelT - model type with `VARIABLE_SIZE` local variables and | ||
| * `CONSTRAINT_SIZE` local constraints |
There was a problem hiding this comment.
following what i did here, i'd recommend establishing these requirements on the ModelT parameter as a concept to reduce the amount of error noise for the end user
| * @param[in] col_indices - Global column of each local column | ||
| * @param[in,out] entries - Entries in global indices | ||
| */ | ||
| [[maybe_unused]] __attribute__((always_inline)) static void mapped_store(double val, |
There was a problem hiding this comment.
| [[maybe_unused]] __attribute__((always_inline)) static void mapped_store(double val, | |
| [[maybe_unused, gnu::always_inline]] static void mapped_store(double val, |
| static constexpr IdxT INTERNAL_VARIABLE_SIZE = 0; | ||
| /// Internal constraint functions: squared apparent power at bus 1 and bus 2 | ||
| static constexpr IdxT INTERNAL_CONSTRAINT_SIZE = 2; | ||
| static constexpr bool CONSTANT = false; |
There was a problem hiding this comment.
i would like you to document the meaning of this parameter somewhere. one place to do that would be on the concept that i proposed in another review comment for compile-time validation of optimal power flow components
| /** | ||
| * @brief Line or off-nominal transformer between two buses | ||
| * | ||
| * Uses the admittance of `PhasorDynamics::Branch`. Power is positive |
There was a problem hiding this comment.
"uses the admittance of PhasorDynamics::Branch" and similar comments referencing how the modeling code is based off that in the phasor dynamics modeling library are not worth retaining as they could diverge in the future, imo
| const auto entry = actual.find(coordinate); | ||
| if (entry == actual.end()) | ||
| { | ||
| std::cerr << "Entry (" << coordinate.first << ", " << coordinate.second << ") is missing\n"; |
There was a problem hiding this comment.
you should be using the logger here and elsewhere in these tests
|
|
||
| auto& generator = data.generator.emplace_back(); | ||
| generator.id = std::format("gen_{}", i + 1); | ||
| generator.in_service = row.status > 0; |
There was a problem hiding this comment.
because SystemModelData parameterizes the power flow data types with size_t, -1 (which is a valid status value) will be converted to a positive value, which will misinterpret the model.
possible solutions:
- don't use matpower, use our own json modeling capabilities which could represent it as a boolean
- change how status is represented, don't tie it to
IdxT, probably parse it to a boolean but read it asintor something
nkoukpaizan
left a comment
There was a problem hiding this comment.
Overall nice work! The component implementations are clear and I like the consolidated ComponentModelImpl.hpp.
A few comments:
- For Enzyme, can we re-use some of the existing boilerplate for
LowerSparseStorageand use buffers instead ofCooEntrieswrappingstd::vector)? Feel free to amend the wrappers. - Can we bind
GridKit::LinearAlgebra::Vectorat the component level instead ofstd::vector? This could be a follow-up PR. - Several methods, e.g., in
OptimizationEvaluator*andSteadyState*don't have a Doxygen documentation.
| static constexpr RealT POWER_TOL = 2 * std::numeric_limits<RealT>::epsilon(); | ||
| static constexpr RealT JACOBIAN_TOL = 2 * std::numeric_limits<RealT>::epsilon(); | ||
| static constexpr RealT HESSIAN_TOL = 9 * std::numeric_limits<RealT>::epsilon(); |
There was a problem hiding this comment.
I would round to 10 * and use the same tolerance.
| if(GRIDKIT_ENABLE_ENZYME) | ||
| add_subdirectory(OptimalPowerFlow) | ||
| endif() |
There was a problem hiding this comment.
I would move the guard into the OptimalPowerFlow directory.
| static constexpr RealT DERIVATIVE_TOL = 8 * std::numeric_limits<RealT>::epsilon(); | ||
| static constexpr RealT BALANCE_TOL = 1.0e-12; |
There was a problem hiding this comment.
Same here, I would round 8 to 10. And replace 1e-12 with a multiple of std::numeric_limits<RealT>::epsilon();
| bool isZero(double value) | ||
| { | ||
| return !(value < ZERO<double>) && !(value > ZERO<double>); | ||
| } |
There was a problem hiding this comment.
We've been trying to avoid these. If something needs == 0/!= 0, please highlight and we can evaluate.
| return true; | ||
| } | ||
| std::cerr << std::setprecision(std::numeric_limits<RealT>::max_digits10) << value << " differs from " | ||
| << reference << " by " << std::abs(value - reference) / (ONE<RealT> + std::abs(reference)) / std::numeric_limits<RealT>::epsilon() |
There was a problem hiding this comment.
I wouldn't re-implement Testing::isEqual.
| const RealT* x = system.x().getData(); | ||
|
|
||
| success *= isEqual(x[1], ZERO<RealT>, BALANCE_TOL); | ||
| success *= std::abs(x[3]) > BALANCE_TOL && std::abs(x[5]) > BALANCE_TOL; |
There was a problem hiding this comment.
Is checking the absolute values really needed instead of isEqual?
| is >> br.fbus >> br.tbus >> br.r >> br.x >> br.b >> br.rateA >> br.rateB | ||
| >> br.rateC >> br.ratio >> br.angle >> br.status >> br.angmin | ||
| >> br.angmax; | ||
| checkEndOfMatrixRow(is); |
There was a problem hiding this comment.
Removing checkEndOfMatrixRow partially addresses #587, but we still need to ensure the read was successful (e.g., row not too short).
pelesh
left a comment
There was a problem hiding this comment.
This looks very good and has clean component model structure. Main issues that I would raise are:
- Separating equality and inequality constraint evaluation methods.
- Selection of coordinates polar vs. Cartesian.
- Inheritance patterns should mirror
PhasorDynamicsmodule. If we want to change it, then the change should made be across the GridKit modules.
| * Filled by `mapped_store` and `lower_store`. Assembly sums duplicate | ||
| * coordinates. | ||
| */ | ||
| struct CooEntries |
There was a problem hiding this comment.
What is the purpose of this struct? This needs to be documented.
| using ScalarT = scalar_type; | ||
| using IdxT = index_type; | ||
| using RealT = typename ComponentModelT::RealT; | ||
| using ModelDataT = BranchData<RealT, IdxT>; |
There was a problem hiding this comment.
These should not be public. ScalarT, IdxT, etc. are local type aliases.
| g[0] = p1 * p1 + q1 * q1; | ||
| g[1] = p2 * p2 + q2 * q2; | ||
| g[2] = p1; | ||
| g[3] = q1; | ||
| g[4] = p2; | ||
| g[5] = q2; |
There was a problem hiding this comment.
Need to specify here what are equality and what are inequality constraints.
| * @tparam model_type - Derived component model | ||
| */ | ||
| template <typename model_type, typename scalar_type, typename index_type> | ||
| class ComponentModel : public Component<scalar_type, index_type> | ||
| { | ||
| public: |
There was a problem hiding this comment.
I suggest removing Component class and using the same inheritance structure as in PhasorDynamics module.
| const auto& terminals = component.terminals(); | ||
| for (IdxT t = 0; t < terminals.size(); ++t) | ||
| { |
There was a problem hiding this comment.
I believe that we decided to use term "ports" rather than "terminals". It would be good to use consistent nomenclature across all GridKit modules.
| Number* x_u, | ||
| Index m, |
There was a problem hiding this comment.
These are deprecated Ipopt types. Use ipnumber and ipindex instead.
| template <typename scalar_type, typename index_type> | ||
| class SteadyState : public Ipopt::TNLP | ||
| { |
There was a problem hiding this comment.
This is not SteadyState but rather Ipopt specific interface. Perhaps use SteadyState for the namespace and IpoptInterface for the class name.
| public: | ||
| using ScalarT = scalar_type; | ||
| using IdxT = index_type; | ||
| using RealT = typename ScalarTraits<ScalarT>::RealT; | ||
| using CooEntriesT = Enzyme::Sparse::CooEntries; |
There was a problem hiding this comment.
These type aliases should not be public.
| std::vector<std::unique_ptr<ComponentT>> components_; | ||
|
|
||
| /// Buses by number | ||
| std::map<IdxT, BusT*> buses_; | ||
|
|
||
| /// Map from component entries to CSR entries | ||
| std::vector<IdxT> jacobian_map_; | ||
| std::vector<IdxT> hessian_map_; |
There was a problem hiding this comment.
I would avoid using STL containers for system level objects. If it is a temporary solution, it should be marked as such.
| virtual int evaluateConstraints() = 0; | ||
| virtual int evaluateJacobian() = 0; |
There was a problem hiding this comment.
It would be good to separate equality and inequality constraints, as well as their respective Jacobians.
38dc4b5 to
5bc7404
Compare
|
Thank you all for detailed review, I believe I have addressed all except a distinct few. I will go response by response soon, but the overall design is overall more consistent with PhasorDynamics except for the inheritance structure. I am considering removing the larger ACTIVSg2000 case until after this is merged, please let me know what you think. |
Description
Experimental @superwhiskers @nkoukpaizan which uses MATPOWER input which makes interfacing with
PhasorDynamicsharder but mostOPFdata comes in MATPOWER, so this is probably most practical choiceOptimalDispatchChecklist
-Wall -Wpedantic -Wconversion -Wextra.Further comments
Needs some cleaning first